/*
 * Copyright (c) Huawei Device Co., Ltd. 2023. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "se_service.h"

#include <thread>
#include <condition_variable>

#include "ipc_object_stub.h"
#include "isecure_element_reader.h"
#include "iservice_registry.h"
#include "loghelper.h"
#include "reader.h"
#include "service_load_callback.h"
#include "system_ability_definition.h"

namespace OHOS {
namespace SE {
namespace OMAPI {
static const int SECURE_ELEMENT_LOAD_SA_TIMEOUT_MS = 4000; // system allows max time:4s

SEService::SeServiceDeathCallback g_seDeathcallback;

class ISeServiceStub : public OHOS::IRemoteBroker {
public:
    DECLARE_INTERFACE_DESCRIPTOR(u"ohos.se.omapi.ISeServiceStub");
    virtual ~ISeServiceStub() {}
};

class SeServiceStub : public OHOS::IRemoteStub<ISeServiceStub> {
public:
    DECLARE_INTERFACE_DESCRIPTOR(u"ohos.se.omapi.ISeServiceStub");
    virtual ~SeServiceStub()
    {}
};

SEService::SEService()
{
    InfoLog("SEService::SEService");
    deathRecipient_ = new (std::nothrow) SeServiceDeathRecipient(this);
}

SEService::~SEService()
{
    InfoLog("SEService::~SEService");
}

void SEService::SeServiceDeathRecipient::OnRemoteDied(const wptr<IRemoteObject> &remoteObject)
{
    std::lock_guard<std::mutex> lock(mutex_);
    if (client_) {
        client_->OnRemoteDied(remoteObject);
    } else {
        ErrorLog("client_ is nullptr!");
    }
}

void SEService::OnRemoteDied(const wptr<IRemoteObject> &remoteObject)
{
    std::lock_guard<std::mutex> lock(mutex_);
    WarnLog("Remote service is died!");
    if (deathRecipient_ == nullptr || remoteObject == nullptr) {
        ErrorLog("deathRecipient_ is nullptr!");
        return;
    }
    if (remote_ == nullptr) {
        ErrorLog("remote_ is nullptr!");
        return;
    }

    remote_->RemoveDeathRecipient(deathRecipient_);

    // notify fwk se service death
    if (g_seDeathcallback != nullptr) {
        g_seDeathcallback();
    }
}

void SEService::HandleLoadServiceFinished(const sptr<IRemoteObject>& remoteObject, bool success)
{
    InfoLog("HandleLoadServiceFinished, success = %{public}d", success);
    std::unique_lock<std::mutex> lock(mutex_);
    serviceProxy_ = nullptr;
    if (success && remoteObject) {
        remote_ = remoteObject;
        if (deathRecipient_ != nullptr) {
            remote_->AddDeathRecipient(deathRecipient_);
        }
        serviceProxy_ = new (std::nothrow) ServiceProxy(remoteObject);
        if (serviceProxy_ == nullptr) {
            ErrorLog("serviceProxy_ is nullptr");
            return;
        }
    }
    // notify the waitting thread to exit.
    serviceLoadedReadyFlag_ = true;
    seConditionVariable_.notify_one();
}

int SEService::InitSEService(bool &initStatus, SeServiceDeathCallback callback)
{
    sptr<ISystemAbilityManager> systemAbilityMgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
    {
        std::unique_lock<std::mutex> lock(mutex_);
        g_seDeathcallback = callback;
        remote_ = systemAbilityMgr->CheckSystemAbility(SE_MANAGER_SYS_ABILITY_ID);
        if (remote_ != nullptr) {
            // this sa is already running, don't load again.
            if (deathRecipient_ != nullptr) {
                remote_->AddDeathRecipient(deathRecipient_);
            }
            WarnLog("InitSEService::CheckSystemAbility, already running.");
            serviceProxy_ = new (std::nothrow) ServiceProxy(remote_);
            if (serviceProxy_ == nullptr) {
                ErrorLog("serviceProxy_ is nullptr");
                return ErrorCode::SE_ILLEGAL_STATE_ERROR;
            }
            serviceImpl_ = serviceProxy_;
            serviceImpl_.promote()->RegisterCallerListener(new SeServiceStub());
            initStatus = true;
            isConnected_ = true;
            return ErrorCode::SE_SUCCESS;
        }
    }
    // se service not running, load it and wait success.
    sptr<ServiceLoadCallback> seSaLoadCallback = new ServiceLoadCallback(shared_from_this());
    if (seSaLoadCallback == nullptr) {
        ErrorLog("InitSEService, new ServiceLoadCallback failed!");
        initStatus = false;
        return ErrorCode::SE_ILLEGAL_STATE_ERROR;
    }
    int32_t status = systemAbilityMgr->LoadSystemAbility(SE_MANAGER_SYS_ABILITY_ID, seSaLoadCallback);
    if (status != ERR_OK) {
        ErrorLog("InitSEService, LoadSystemAbility failed! status = %{public}d", status);
        initStatus = false;
        return ErrorCode::SE_ILLEGAL_STATE_ERROR;
    }

    {
        std::unique_lock<std::mutex> lock(mutex_);
        InfoLog("InitSEService, wait_for loading begin.");
        auto waitSuccess = seConditionVariable_.wait_for(
            lock,
            std::chrono::milliseconds(SECURE_ELEMENT_LOAD_SA_TIMEOUT_MS), [this]() { return serviceLoadedReadyFlag_; });
        if (!waitSuccess) {
            // time out or loadcallback fail
            ErrorLog("InitSEService, LoadSystemAbility timeout");
            initStatus = false;
            return ErrorCode::SE_ILLEGAL_STATE_ERROR;
        }

        if (!serviceProxy_) {
            ErrorLog("InitSEService, LoadSystemAbility, serviceProxy_ null");
            initStatus = false;
            return ErrorCode::SE_ILLEGAL_STATE_ERROR;
        }
        serviceImpl_ = serviceProxy_;
        serviceImpl_.promote()->RegisterCallerListener(new SeServiceStub());
    }

    InfoLog("InitSEService, secure service started success.");
    initStatus = true;
    isConnected_ = true;
    return ErrorCode::SE_SUCCESS;
}

int SEService::IsConnected(bool &isConnected)
{
    std::lock_guard<std::mutex> guard(mutex_);
    if (!isConnected_) {
        isConnected = false;
        return ErrorCode::SE_SUCCESS;
    }
    if (serviceProxy_ == nullptr || serviceImpl_.promote() == nullptr) {
        isConnected = false;
    } else {
        isConnected = true;
    }
    InfoLog("SEService::IsConnected = %{public}d.", isConnected);
    return ErrorCode::SE_SUCCESS;
}

int SEService::GetReaders(std::vector<std::shared_ptr<Reader>>& readerList)
{
    bool isConnected = false;
    IsConnected(isConnected);
    if (!isConnected) {
        ErrorLog("GetReaders, service is disconnected.");
        return ErrorCode::SE_ILLEGAL_STATE_ERROR;
    }

    std::lock_guard<std::mutex> guard(mutex_);
    std::vector<std::string> readerNameList;
    int ret = serviceImpl_.promote()->GetReaders(readerNameList);
    for (auto readerName : readerNameList) {
        InfoLog("GetReaders, Reader of (%{public}s).", readerName.c_str());
        if (readerName.empty()) {
            continue;
        }

        // if the readerName hasn't the relational Reader, create the new Reader.
        std::unordered_map<std::string, std::shared_ptr<Reader>>::const_iterator readerTmp = readers_.find(readerName);
        if (readerTmp == readers_.end()) {
            sptr<ISecureElementReader> reader = nullptr;
            ret = serviceImpl_.promote()->GetReaderInterface(readerName, reader);
            if (reader != nullptr) {
                std::shared_ptr<Reader> newReader = std::make_shared<Reader>(shared_from_this(), readerName, reader);
                readers_.insert(std::make_pair(readerName, newReader));
            }
        }
    }

    for (auto reader : readers_) {
        readerList.push_back(reader.second);
    }
    return ret;
}

int SEService::Shutdown(void)
{
    InfoLog("SEService::Shutdown.");
    bool isConnected = false;
    IsConnected(isConnected);
    if (!isConnected) {
        ErrorLog("SEService::Shutdown service is disconnected.");
        return ErrorCode::SE_ILLEGAL_STATE_ERROR;
    }

    {
        std::lock_guard<std::mutex> guard(mutex_);
        for (auto reader : readers_) {
            if (reader.second) {
                reader.second->CloseSessions();
            }
        }
        readers_.clear();
        int ret = serviceImpl_.promote()->Shutdown();
        if (ret != ErrorCode::SE_SUCCESS) {
            ErrorLog("SEService::Shutdown failed.");
            return ErrorCode::SE_ILLEGAL_STATE_ERROR;
        }
        isConnected_ = false;
    }
    InfoLog("SEService::Shutdown done.");
    return ErrorCode::SE_SUCCESS;
}

int SEService::GetVersion(std::string& version)
{
    version = "3.3";
    return ErrorCode::SE_SUCCESS;
}
}  // namespace OMAPI
}  // namespace SE
}  // namespace OHOS
